package com.bitrate.dagger.redis.aspect;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.bitrate.dagger.common.util.GetClientIpUtil;
import com.bitrate.dagger.redis.anno.XRateLimit;
import com.bitrate.dagger.redis.enums.LimitStrategy;
import com.bitrate.dagger.redis.exception.RateLimitException;


/**
 * 限流
 */
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE-1)
public class RateLimitAop {

	@Autowired
	private RedissonClient redissonClient;

	@Around("@annotation(xRateLimit)")
	public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint,
			XRateLimit xRateLimit) throws Throwable {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		// 限流策略
		LimitStrategy strategy = xRateLimit.strategy();
		// 时间策略
		RateIntervalUnit rateUnit = xRateLimit.rateUnit();
		// 共享策略
		RateType type = xRateLimit.type();
		// 产生可用令牌数量
		long rate = xRateLimit.rate();
		// 流速
		long limit = xRateLimit.limit();
		// 访问请求路径
		String requestURI = request.getRequestURI();
		String rateLimitName = "";
		switch (strategy) {
		case GLOBAL:
			rateLimitName = requestURI;
			break;
		case IP:
			String remoteIp = GetClientIpUtil.getIp(request);
			rateLimitName = requestURI + "/" + remoteIp;
			break;
		default:
			break;
		}
		RRateLimiter rateLimiter = redissonClient.getRateLimiter(rateLimitName);
		rateLimiter.trySetRate(type, rate, limit, rateUnit);
		if (rateLimiter.tryAcquire()) {
			Object obj = proceedingJoinPoint.proceed();
			return obj;
		} else {
			throw new RateLimitException(xRateLimit.warnMsg());
		}
	}

}
